<script lang="ts">
// 组件文件名 /src/components/c-identify.vue
import Vue, { VueConstructor } from "vue";
/**
 * 组件-验证码
 * @author [xingsk]
 * @interface CIdentify
 * @method changeCode() 修改验证码
 * @split ---------------------------------------
 */
export interface CIdentify extends VueConstructor {
    /**
     * 修改验证码
     */
    changeCode(): void;
}
/**
 * 组件-验证码
 * @component CIdentify
 * @extends Vue
 * @property { string } identifyCodes 验证码从该字段中抽取生成(ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678)
 * @property { number } fontSizeMin 字体最小值(25)
 * @property { number } fontSizeMax 字体最大值(35)
 * @property { number } backgroundColorMin 验证码图片背景色最小值(200)
 * @property { number } backgroundColorMax 验证码图片背景色最大值(220)
 * @property { number } dotColorMin 背景干扰点最小值(60)
 * @property { number } dotColorMax 背景干扰点最大值(120)
 * @property { number } contentWidth 容器宽度(100)
 * @property { number } contentHeight 容器宽度(30)
 *
 * @event change 修改后的验证码
 */
export const CIdentify = Vue.extend<
    {
        /**
         * 验证码
         */
        identifyCode: string;
        /**
         * 验证码长度
         */
        codeLength: number;
    },
    {
        /**
         * 生成一个随机数
         * @param min 最小值
         * @param max 最大值
         * @returns
         */
        randomNum(min: number, max: number): number;
        /**
         * 生成一个随机的颜色
         * @param min 最小值
         * @param max 最大值
         * @returns
         */
        randomColor(min: number, max: number): string;
        /**
         * 生成验证码
         */
        drawPic(): void;
        /**
         * 绘制文字
         * @param ctx 画布
         * @param txt 文字
         * @param i 索引
         */
        drawText(ctx: CanvasRenderingContext2D, txt: string, i: number): void;
        /**
         * 绘制干扰线
         * @param ctx 画布
         */
        drawLine(ctx: CanvasRenderingContext2D): void;
        /**
         * 绘制干扰点
         * @param ctx 画布
         */
        drawDot(ctx: CanvasRenderingContext2D): void;
        /**
         * 切换验证码
         */
        changeCode(): void;
        /**
         * 生成验证码
         * @param e 验证码
         * @param n 验证码长度
         */
        makeCode(e: string, n: number): void;
    },
    {},
    {
        /**
         * 验证码从该字段中抽取生成
         * @default 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
         */
        identifyCodes: string;
        /**
         * 字体最小值
         * @default 25
         */
        fontSizeMin: number;
        /**
         * 字体最大值
         * @default 35
         */
        fontSizeMax: number;
        /**
         * 验证码图片背景色最小值
         * @default 200
         */
        backgroundColorMin: number;
        /**
         * 验证码图片背景色最大值
         * @default 220
         */
        backgroundColorMax: number;
        /**
         * 背景干扰点最小值
         * @default 60
         */
        dotColorMin: number;
        /**
         * 背景干扰点最大值
         * @default 120
         */
        dotColorMax: number;
        /**
         * 容器宽度
         * @default 100
         */
        contentWidth: number;
        /**
         * 容器高度
         * @default 30
         */
        contentHeight: number;
    }
>({
    name: "c-identify",
    props: {
        identifyCodes: {
            type: String,
            default: "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678",
        },
        fontSizeMin: {
            type: Number,
            default: 25,
        },
        fontSizeMax: {
            type: Number,
            default: 35,
        },
        backgroundColorMin: {
            type: Number,
            default: 200,
        },
        backgroundColorMax: {
            type: Number,
            default: 220,
        },
        dotColorMin: {
            type: Number,
            default: 60,
        },
        dotColorMax: {
            type: Number,
            default: 120,
        },
        contentWidth: {
            type: Number,
            default: 100,
        },
        contentHeight: {
            type: Number,
            default: 30,
        },
    },
    data() {
        return {
            identifyCode: "", // 验证码
            codeLength: 4, // 验证码长度
        };
    },
    watch: {
        /**
         * 监听验证码被改变后需要重新绘制图像
         */
        identifyCode() {
            this.drawPic();
        },
    },
    mounted() {
        this.drawPic();
        this.makeCode(this.identifyCodes, 4);
    },
    methods: {
        randomNum(min: number, max: number) {
            return Math.floor(Math.random() * (max - min) + min);
        },
        randomColor(min: number, max: number) {
            let r = this.randomNum(min, max);
            let g = this.randomNum(min, max);
            let b = this.randomNum(min, max);
            return "rgb(" + r + "," + g + "," + b + ")";
        },
        drawPic() {
            let canvas = document.getElementById(
                "cont-canvas"
            ) as HTMLCanvasElement;
            let ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
            ctx.textBaseline = "bottom";
            // 绘制背景
            ctx.fillStyle = this.randomColor(
                this.backgroundColorMin,
                this.backgroundColorMax
            );
            ctx.fillRect(0, 0, this.contentWidth, this.contentHeight);
            // 绘制文字
            for (let i = 0; i < this.identifyCode.length; i++) {
                this.drawText(ctx, this.identifyCode[i], i);
            }
            this.drawLine(ctx);
            this.drawDot(ctx);
        },
        drawText(ctx: CanvasRenderingContext2D, txt: string, i: number) {
            ctx.fillStyle = this.randomColor(50, 160); //随机生成字体颜色
            ctx.font =
                this.randomNum(this.fontSizeMin, this.fontSizeMax) +
                "px SimHei"; //随机生成字体大小
            let x =
                (i + 1) * (this.contentWidth / (this.identifyCode.length + 1));
            let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5);
            var deg = this.randomNum(-30, 30);
            // 修改坐标原点和旋转角度
            ctx.translate(x, y);
            ctx.rotate((deg * Math.PI) / 180);
            ctx.fillText(txt, 0, 0);
            // 恢复坐标原点和旋转角度
            ctx.rotate((-deg * Math.PI) / 180);
            ctx.translate(-x, -y);
        },

        drawLine(ctx: CanvasRenderingContext2D) {
            // 绘制干扰线
            for (let i = 0; i < 4; i++) {
                ctx.strokeStyle = this.randomColor(100, 200);
                ctx.beginPath();
                ctx.moveTo(
                    this.randomNum(0, this.contentWidth),
                    this.randomNum(0, this.contentHeight)
                );
                ctx.lineTo(
                    this.randomNum(0, this.contentWidth),
                    this.randomNum(0, this.contentHeight)
                );
                ctx.stroke();
            }
        },
        drawDot(ctx: CanvasRenderingContext2D) {
            // 绘制干扰点
            for (let i = 0; i < 30; i++) {
                ctx.fillStyle = this.randomColor(0, 255);
                ctx.beginPath();
                ctx.arc(
                    this.randomNum(0, this.contentWidth),
                    this.randomNum(0, this.contentHeight),
                    1,
                    0,
                    2 * Math.PI
                );
                ctx.fill();
            }
        },
        changeCode() {
            this.identifyCode = "";
            this.makeCode(this.identifyCodes, 4);
        },
        makeCode(e: string, n: number) {
            for (let i = 0; i < n; i++) {
                this.identifyCode += e[this.randomNum(0, e.length)];
            }
            this.$emit("change", this.identifyCode);
        },
    },
    render(h) {
        return h(
            "div",
            {
                staticClass: "x-canvas-box",
                staticStyle: {
                    overflow: "hidden",
                },
                style: {
                    width: this.contentWidth + "px",
                    height: this.contentHeight + "px",
                },
                on: {
                    click: () => this.changeCode(),
                },
            },
            [
                h("canvas", {
                    attrs: {
                        id: "cont-canvas",
                        width: this.contentWidth,
                        height: this.contentHeight,
                    },
                }),
            ]
        );
    },
});
export default CIdentify;
</script>
